import * as THREE from '../../build/three.module.js';
import Stats from '../../jsm/libs/stats.module.js';
import { GeometryUtils } from '../../jsm/utils/GeometryUtils.js';

let font = null,
    size = 70,
    height = 20,
    hover = 30, // 文字模型1 y轴坐标
    fontIndex = 0,
    curveSegments = 4,
    bevelThickness = 2,
    bevelSize = 1.5,
    bevelEnabled = true,
    fontName = 'optimer',
    fontWeight = 'bold',

    mouseXOnMouseDown = 0,
    mouseX = 0,
    windowHalfX = innerWidth / 2,
    windowHalfY = innerHeight / 2,

    targetRotationOnMouseDown = 0,
    targetRotation = 0,
    
    textMesh1,
    textMesh2,
    text = 'three.js',
    mirror = true;

const fontMap = {
  "helvetiker": 0,
  "optimer": 1,
  "gentilis": 2,
  "droid/droid_sans": 3,
  "droid/droid_serif": 4
}
const reverseFontMap = [];
for(let i in fontMap) reverseFontMap[ fontMap[i] ] = i;

const weightMap = {
  "regular": 0,
  "bold": 1
}
const reverseWeightMap = [];
for ( var i in weightMap ) reverseWeightMap[ weightMap[ i ] ] = i;

THREE.Cache.enabled = true; //在所有使用FileLoader的加载器上启用缓存

const scene = new THREE.Scene();
scene.background = new THREE.Color(0x000000);
scene.fog = new THREE.Fog(0x000000, 250, 1400);

const camera = new THREE.PerspectiveCamera(30, innerWidth / innerHeight, 1, 1500);
camera.position.set(0, 480, 840);
const cameraTerget = new THREE.Vector3(0, 150, 0);

const dirtLight = new THREE.DirectionalLight(0xfffff, .125);
dirtLight.position.set(0, 0, 1).normalize();
scene.add(dirtLight);

const pointLight = new THREE.PointLight(0xffffff, 1.5);
pointLight.position.set(0, 100, 90);
scene.add(pointLight);

const mtls = [
  new THREE.MeshPhongMaterial({ color: 0xffffff, flatShading: true }), // 文字本身
  new THREE.MeshPhongMaterial({ color: 0xffffff }) // 倒影
]

const group = new THREE.Group();
group.position.y = 200;
scene.add(group);

const plane = new THREE.Mesh(
  new THREE.PlaneBufferGeometry(10000, 10000),
  new THREE.MeshBasicMaterial({ color: 0xffffff, opacity: .5, transparent: true })
)
plane.position.y = 200;
plane.rotation.x = - Math.PI / 2;
scene.add(plane);

const renderer = new THREE.WebGLRenderer({ antialias: true });
renderer.setPixelRatio(devicePixelRatio);
renderer.setSize(innerWidth, innerHeight);
document.body.appendChild(renderer.domElement);

const stats = new Stats();

// 功能函数
// 创建字体模型
const createText = () => {
  let textGmt = new THREE.TextGeometry(text, {
    font: font,

    size: size,
    height: height,
    curveSegments: curveSegments,

    bevelThickness: bevelThickness,
    bevelSize: bevelSize,
    bevelEnabled: bevelEnabled
  });
  textGmt.computeBoundingBox();
  textGmt.computeVertexNormals();

  // 通过删除z轴法线，来修复侧面的侧面法线
  // 在启用斜切时，无需这样的操作，因为会丢失环绕z轴的良好曲率
  if (!bevelEnabled) {
    const triangleAreaHeuristics = .1 * (height * size);
    for(let i = 0; i < textGmt.faces.length; i += 1) {
      const face = textGmt.faces[i];
      if (face.materialIndex === 1) {
        for(let j = 0; j < face.vertexNormals.length; j += 1) {
          face.vertexNormals[j].z = 0;
          face.vertexNormals[j].normalize();
        }
        const va = textGmt.vertices[face.a];
        const vb = textGmt.vertices[face.b];
        const vc = textGmt.vertices[face.c];
        const s = GeometryUtils.triangleArea(va, vb, vc);
        if (s > triangleAreaHeuristics) {
          for(let j = 0; j < face.vertexNormals.length; j += 1) {
            face.vertexNormals[j].copy(face.normal);
          }
        }
      }
    }
  }

  const centerOffset = -.5 * (textGmt.boundingBox.max.x - textGmt.boundingBox.min.x);
  textGmt = new THREE.BufferGeometry().fromGeometry(textGmt);
  textMesh1 = new THREE.Mesh(textGmt, mtls);
  textMesh1.position.x = centerOffset;
  textMesh1.position.y = hover;
  textMesh1.position.z = 0;
  textMesh1.rotation.x = 0;
  textMesh1.rotation.y = Math.PI * 2;

  group.add(textMesh1);

  if (mirror) {
    textMesh2 = new THREE.Mesh(textGmt, mtls);
    textMesh2.position.x = centerOffset;
    textMesh2.position.y = - hover;
    textMesh2.position.z = height;

    textMesh2.rotation.x = Math.PI;
    textMesh2.rotation.y = Math.PI * 2;

    group.add( textMesh2 );
  }
}

// 刷新字体
const refreshText = () => {
  group.remove(textMesh1);
  if (mirror) group.remove(textMesh2);
  if (!text) return;
  createText();
}

// 加载字体
const loadFont = () => {
  const loader = new THREE.FontLoader();
  loader.load(`../../fonts/${fontName}_${fontWeight}.typeface.json`, (res) => {
    font = res;
    refreshText();
  })
}
loadFont();

// 事件绑定与处理
document.getElementById('color').addEventListener('click', () => {
  pointLight.color.setHSL(Math.random(), 1, .5);
}, false);

document.getElementById('font').addEventListener('click', () => {
  fontIndex++;
  fontName = reverseFontMap[ fontIndex % reverseFontMap.length ];
  loadFont();
}, false);

document.getElementById('weight').addEventListener('click', () => {
  if (fontWeight === 'bold') {
    fontWeight = 'regular';
  } else {
    fontWeight = 'bold';
  }
  loadFont();
}, false);

document.getElementById('bevel').addEventListener('click', () => {
  bevelEnabled = !bevelEnabled;
  refreshText();
}, false);

const onDomMouseMove = (e) => {
  mouseX = e.clientX - windowHalfX;
  targetRotation = targetRotationOnMouseDown + (mouseX - mouseXOnMouseDown) * .02;
}
const onDomMouseUp = () => {
  document.removeEventListener( 'mousemove', onDomMouseMove, false );
  document.removeEventListener( 'mouseup', onDomMouseUp, false );
  document.removeEventListener( 'mouseout', onDomMouseOut, false );
}
const onDomMouseOut = () => {
  document.removeEventListener( 'mousemove', onDomMouseMove, false );
  document.removeEventListener( 'mouseup', onDomMouseUp, false );
  document.removeEventListener( 'mouseout', onDomMouseOut, false );
}
const onDomMouseDown = (e) => {
  e.preventDefault();

  document.addEventListener('mousemove', onDomMouseMove, false);
  document.addEventListener('mouseup', onDomMouseUp, false);
  document.addEventListener('mouseout', onDomMouseOut, false);

  mouseXOnMouseDown = e.clientX - windowHalfX;
  targetRotationOnMouseDown = targetRotation;
}

document.addEventListener('mousedown', onDomMouseDown, false);

const render = () => {
  group.rotation.y += (targetRotation - group.rotation.y) * .05;

  camera.lookAt(cameraTerget);

  renderer.clear();
  renderer.render(scene, camera);
}

const animate = () => {
  requestAnimationFrame(animate);

  stats.update();
  render();
}
animate();